﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using CarolLib.FTP;
using DCS.Business.Event;
using DCS.Business.Interface;
using DCS.Business.Manager;
using DCS.Business.Model;
using DCS.DataProvider;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using DCS.Configuration;
using System.Net;

namespace DCS.BusinessObject
{
    public class Downloader : IFTPDownloader
    {
        #region Members

        static FTPDownloadSetting Settings = FTPDownloadConfiguration.GetConfig().Settings;
        static log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        static DCSFileManager manager;
        static MonitorManager momanager;
        Thread downloadThread;
        Thread cleanThread;
        Thread faultFixThread;
        Thread deleteCleanMsg;

        #endregion

        #region IFTPDownloader Members

        public void Init()
        {
            IUnityContainer myContainer = new UnityContainer();
            UnityConfigurationSection section
              = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers["Data"].Configure(myContainer);

            IDataProvider dp = myContainer.Resolve<IDataProvider>();

            manager = new DCSFileManager(dp);
            momanager = new MonitorManager(dp);

            downloadThread = new Thread(
                () =>
                {
                    while (true)
                    {
                        Thread.Sleep(Settings.DownloadInterval);
                        this.ExecuteDownload();
                    }
                });
            downloadThread.Start();

            cleanThread = new Thread(
                () =>
                {
                    while (true)
                    {
                        Thread.Sleep(Settings.CleanInterval);
                        this.ExecuteClean();
                    }
                });

            if (Settings.EnableClean)
            {
                cleanThread.Start();
            }

            faultFixThread = new Thread(
                () =>
                {
                    while (true)
                    {
                        Thread.Sleep(Settings.DownloadInterval * 500);
                        this.ExecuteFaultFix();
                    }
                });
            faultFixThread.Start();

            deleteCleanMsg = new Thread(
                () =>
                {
                    while (true)
                    {
                        Thread.Sleep(Settings.DownloadInterval * 500);
                        this.DeleteCleanMessage();
                        this.CleanMessageStatus();
                    }
                });
            deleteCleanMsg.Start();
        }

        public void ExecuteDownload()
        {
            Logger.InfoFormat("Download {0} Message.", Settings.MessageType);
            List<DCSFile> dcsfiles = new List<DCSFile>();
            try
            {
                dcsfiles = manager.GetDCSFilesByMessageType(Settings.MessageType, FileStatus.Detected, 0, FileStatus.StartDownload);


                if (dcsfiles.Count == 0)
                {
#if DEBUG
                    Logger.Info("No recorded files need download.");
#endif
                    return;
                }
                else
                {
                    Logger.InfoFormat("{0} recorded files need download.", dcsfiles.Count);

                    DownloadFiles(dcsfiles);

                    int successCount = manager.UpdateDCSFilesStatus(dcsfiles);
                    Logger.InfoFormat("{0} recorded files have been downloaded.", successCount);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);

                int successCount = manager.UpdateDCSFilesStatus(dcsfiles.Where(d => d.Status == FileStatus.FinishDownload).ToList());
                Logger.InfoFormat("{0} recorded files have been downloaded.", successCount);

                int failedCount = manager.UpdateDCSFilesStatus(dcsfiles.Where(d => d.Status != FileStatus.FinishDownload).ToList());
                Logger.InfoFormat("{0} recorded files downloaded failed.", failedCount);

                Thread.Sleep(1000);
                ExecuteDownload();
            }
        }

        public void ExecuteDetect()
        {
            List<FTPfileInfo> newFiles = null;
            try
            {
                newFiles = DetectNewFiles();
            }
            catch (WebException webex)
            {
                Logger.Error("Error during ExecuteDetect", webex);
                Thread.Sleep(Settings.DownloadInterval);
                return;
            }
            catch (Exception e)
            {
                Logger.Error("Error during ExecuteDetect", e);
                Thread.Sleep(Settings.DownloadInterval);
                return;
            }

            if (newFiles.Count() == 0)
            {
#if DEBUG
                Logger.Info("No new files detected.");
#endif
                return;
            }
            else
            {
                Logger.InfoFormat("Start record {0} new files", newFiles.Count);
            }

            List<DCSFile> fileList = new List<DCSFile>();
            DCSFile dfile;
            foreach (var item in newFiles)
            {
                dfile = new DCSFile();
                dfile.RemoteFileName = item.Filename;
                dfile.RemotePath = item.Path;
                dfile.Size = (int)item.Size;
                dfile.CreateTime = item.FileDateTime;
                dfile.Status = FileStatus.Detected;
                dfile.DetectTime = DateTime.Now;
                dfile.LocalPath = Settings.LocalPath;
                dfile.LocalFileName = item.Filename;

                dfile.MessageType = Settings.MessageType;

                fileList.Add(dfile);
            }

            if (Settings.DownImmediately)
            {
                DownloadFiles(fileList);
            }

            int successCount = manager.DetectNewDCSFiles(fileList);
            Logger.InfoFormat("{0} files have been recorded.", successCount);
        }

        public void ExecuteClean()
        {
            //设置为不删除时，不进行操作
            if (!Settings.EnableClean)
            {
                return;
            }

            Logger.InfoFormat("Clean {0} Message.", Settings.MessageType);

            List<DCSFile> dcsfiles = new List<DCSFile>();
            // todo:此处需要修改，目前没有考虑到276的报文也需要clean
            try
            {
               dcsfiles = manager.GetDCSFilesByMessageType(Settings.MessageType, FileStatus.FinishParse | FileStatus.FinishDownload, 0, 0, true);
               
                //状态为276的文件

               List<DCSFile> syncedFiles = manager.GetDCSFilesByMessageType(Settings.MessageType, FileStatus.ECIFSyncOK | FileStatus.FinishParse | FileStatus.FinishDownload, 0, 0, true);

               dcsfiles.AddRange(syncedFiles);

                if (dcsfiles.Count == 0)
                {
#if DEBUG
                    Logger.Info("No recorded files need clean.");
#endif
                    return;
                }
                else
                {
                    Logger.InfoFormat("{0} recorded files need clean.", dcsfiles.Count);

                    CleanFiles(dcsfiles);

                    int successCount = manager.UpdateDCSFilesStatus(dcsfiles);
                    Logger.InfoFormat("{0} recorded files have been clean.", successCount);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);

                int successCount = manager.UpdateDCSFilesStatus(dcsfiles.Where(d => d.Status == FileStatus.FinishClean).ToList());
                Logger.InfoFormat("{0} recorded files have been clean.", successCount);

                Thread.Sleep(1000);
                ExecuteClean();
            }
        }

        public void ExecuteFaultFix()
        {
            List<DCSFile> needUpdate = new List<DCSFile>();

            try
            {
                List<DCSFile> dcsfiles = manager.GetDCSFilesByMessageType(Settings.MessageType, FileStatus.StartDownload, 0);
                foreach (var item in dcsfiles)
                {
                    if (item.DetectTime.AddMilliseconds(Settings.DownloadInterval * 100) < DateTime.Now)
                    {
                        item.Status = FileStatus.Detected;
                        needUpdate.Add(item);
                    }
                }

                int successCount = manager.UpdateDCSFilesStatus(needUpdate);
                Logger.InfoFormat("{0} recorded files have been FaultFixed.", successCount);
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
        }

        public void DeleteCleanMessage()
        {
            //设置为[不删除]时，不进行操作
            if (!Settings.EnableClean)
            {
                return;
            }


            Dictionary<string, FileStatus> dicFiles = new Dictionary<string, FileStatus>();// = momanager.GetRecentFileStatus(3, Settings.MessageType);

            FTPclient ftp = GetFTPClient();

            FTPdirectory serverList = new FTPdirectory();

            try
            {
                serverList = ftp.ListDirectoryDetail(Settings.RemotePath);
                dicFiles = momanager.GetRecentFileStatus(3, Settings.MessageType);
            }
            catch (Exception e)
            {
                Logger.Error("Error in FTPclient.ListDirectoryDetail", e);
            }

            List<string> deletelist = new List<string>();

            FileStatus fs;
            foreach (var file in serverList)
            {
                if (dicFiles.TryGetValue(file.Filename, out fs))
                {
                    if ((fs & FileStatus.FinishClean) == FileStatus.FinishClean)
                    {
                        deletelist.Add(file.Path + file.Filename);
                    }
                }
            }

            try
            {
                deletelist.ForEach(f => ftp.FtpDeleteAsyc(f));
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
        }

        public void CleanMessageStatus()
        {
            try
            {
                var files = manager.GetDCSFilesByMessageType(Settings.MessageType, FileStatus.FinishDownload | FileStatus.FinishParse, 100, 0, false);

                FTPclient ftp = GetFTPClient();
                FTPdirectory serverList = ftp.ListDirectoryDetail(string.Format("/{0}/", Settings.MessageType));

                foreach (var file in files)
                {
                    // already deleted.
                    if (serverList.Where(f => f.Filename == file.RemoteFileName).Count() == 0)
                    {
                        file.Status = FileStatus.FinishClean;
                    }
                }

                manager.UpdateDCSFilesStatus(files.Where(f => f.Status == FileStatus.FinishClean).ToList());
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
        }

        public void Exit()
        {
            downloadThread.Abort();
            cleanThread.Abort();
            faultFixThread.Abort();
            deleteCleanMsg.Abort();
        }

        #endregion

        #region Private Methods

        #region Old
        //        private List<FTPfileInfo> DetectNewFiles()
        //        {
        //            List<string> recentList = manager.GetRecentFileNames(Settings.RecentDays);
        //            FTPclient ftp = GetFTPClient();

        //            FTPdirectory serverList = ftp.ListDirectoryDetail(Settings.RemotePath);
        //#if DEBUG
        //            Logger.InfoFormat("{0} files/dirs founded", serverList.Count);
        //#endif
        //            var newFiles = serverList.Where(f => !recentList.Contains(f.Filename)
        //                && f.FileType == FTPfileInfo.DirectoryEntryTypes.File
        //                && f.FileDateTime.Date >= DateTime.Today.AddDays(Settings.RecentDays * -1)
        //                && f.FileDateTime < DateTime.Now.AddMinutes(-1)
        //                && f.Filename.EndsWith(".txt")
        //                );

        //            return newFiles.ToList();
        //        }
        #endregion

        /// <summary>
        /// serch new file
        /// </summary>
        /// <returns></returns>
        private List<FTPfileInfo> DetectNewFiles()
        {

            List<FTPfileInfo> resultList = new List<FTPfileInfo>();
            List<string> deleteList = new List<string>();
            Dictionary<string, DateTime> recentList = manager.GetRecentFileNames(Settings.RecentDays);
            if (recentList == null)
            {
                return resultList;
            }

            FTPclient ftp = GetFTPClient();

            //get ftp file list
            FTPdirectory serverList = ftp.ListDirectoryDetail(Settings.RemotePath);
#if DEBUG
            Logger.InfoFormat("{0} files/dirs founded", serverList.Count);
#endif


            foreach (FTPfileInfo serverFile in serverList)
            {
                if ((serverFile.FileType == FTPfileInfo.DirectoryEntryTypes.File) &&
                    (serverFile.FileDateTime.Date >= DateTime.Today.AddDays(Settings.RecentDays * -1)) &&
                    (serverFile.FileDateTime < DateTime.Now.AddMinutes(-1)) &&
                    (serverFile.Filename.EndsWith(".txt")))
                {
                    //数据库中不存在的文件
                    if (!recentList.Keys.Contains(serverFile.Filename))
                    {
                        resultList.Add(serverFile);
                    }
                    else
                    {
                        //数据库中已经存在了，但是时间不相等
                        if (serverFile.FileDateTime.AddMinutes(-1) > recentList[serverFile.Filename])
                        {
                            resultList.Add(serverFile);

                            deleteList.Add(serverFile.Filename);

                           // Logger.Warn(string.Format("{0}  OldTime:{1}  NewTime:{2}", serverFile.Filename, recentList[serverFile.Filename], serverFile.FileDateTime));
                        }
                    }

                }
            }

            manager.DeleteDcsFile(deleteList, Settings.MessageType);

            return resultList;
        }

        private static FTPclient GetFTPClient()
        {
            FTPclient ftp = new FTPclient(Settings.RemoteServer, true, Settings.UserName, Settings.Password);
            return ftp;
        }

        private void DownloadFiles(List<DCSFile> fileList)
        {
            if (ConfigurationManager.AppSettings["DownloadByWC"] != "0")
            {
                DownloadFilesByWebClient(fileList);
                return;
            }

            FTPclient ftp = GetFTPClient();

            foreach (var item in fileList)
            {
                try
                {
                    ftp.Download(item.RemotePath + item.RemoteFileName, Settings.LocalPath + "\\" + item.LocalFileName, true);
                    item.Status = FileStatus.FinishDownload;
                    item.DownloadTime = DateTime.Now;
                }
                catch (Exception e)
                {
                    //ftp.Download(item.RemoteFileName, Settings.LocalPath + "\\" + item.LocalFileName, false);
                    item.Status = FileStatus.DownloadError;
                    Logger.Warn("download fail." + item.RemoteFileName, e);
                }
            }
        }

        /// <summary>
        /// 2011年10月18日添加，尝试解决原有流下载中文件内容错乱的异常
        /// </summary>
        /// <param name="fileList"></param>
        private void DownloadFilesByWebClient(List<DCSFile> fileList)
        {
            using (WebClient wc = new WebClient())
            {
                wc.Credentials = new NetworkCredential(Settings.UserName, Settings.Password);

                foreach (var item in fileList)
                {
                    try
                    {
                        wc.DownloadFile(new Uri("ftp://" + Settings.RemoteServer + item.RemotePath + item.RemoteFileName), Settings.LocalPath + "\\" + item.LocalFileName);
                        item.Status = FileStatus.FinishDownload;
                        item.DownloadTime = DateTime.Now;
                    }
                    catch (Exception e)
                    {
                        item.Status = FileStatus.DownloadError;
                        Logger.Warn("download fail." + item.RemoteFileName, e);
                    }
                }
            }
        }

        private static void CleanFiles(List<DCSFile> fileList)
        {
            FTPclient ftp = GetFTPClient();

            foreach (var item in fileList)
            {
                try
                {  
                    
                    ftp.FtpDelete(item.RemotePath + item.RemoteFileName);
                    item.Status = FileStatus.FinishClean;
                }
                catch
                {
                    //Logger.Warn("delete fail." + item.RemoteFileName);
                }
            }
        }

        #endregion
    }
}
